home *** CD-ROM | disk | FTP | other *** search
- /*
- * memory.c --
- *
- * This file implements a dynamic memory allocation system.
- * It provides procedures to allocate and release storage,
- * and also includes monitoring and tracing features.
- *
- * Copyright 1985 Regents of the University of California
- * All rights reserved.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/mem/memory.c,v 9.6 91/09/10 18:40:12 rab Exp $ SPRITE (Berkeley)";
- #endif /* not lint */
-
-
- #include <stdlib.h>
- #include <mem.h>
- #include <memInt.h>
- #include <sprite.h>
- #include <sync.h>
- #include <stdio.h>
- #undef free
-
- extern void _free _ARGS_((Address blockPtr));
-
- /*
- * If the MEM_TRACE flag is defined, extra code will be compiled to allow
- * programs to trace Mem_Alloc and Mem_Free calls.
- #define MEM_TRACE
- */
-
- /*
- * WARNING: several of the macros in this file expect both integers and
- * pointers to be 32 bits (4 bytes) long.
- */
-
- /*
- * This storage allocator consists of two independent allocators,
- * one used for binned objects (<= BIN_SIZE bytes) and the other
- * used for large objects.
- *
- * Both allocators deal with "blocks" of storage, which correspond
- * roughly to the areas that users request and free. Each block of
- * storage consists of one administrative word followed by the
- * useable area. All allocations are done in even numbers of words,
- * which means that clients may occasionally get more bytes than
- * they asked for. Mem_Alloc returns the address of the word just
- * after the administrative word. The administrative word allows us
- * to keep track of which blocks are in use and which are free, and also
- * keeps track of the sizes of blocks so clients don't have to know how
- * large a block is when they free it. The low-order bit of the
- * administrative word indicates whether or not the block is free, and
- * the rest of the word tells how large the block is (in bytes
- * including the administrative word). The next-to-low-order bit
- * of each block is used to indicate that this is a "dummy block"(see
- * the comments for the large-object allocator). Using the low-order
- * bits for flags works because all blocks that we allocate are always
- * multiples of 4 bytes in length. There is one exception to this
- * usage of the administrative word, which occurs for free binned objects.
- * In this case the word is a link to the next free binned object instead
- * of a size. The macros below define the format of the administrative
- * words and how to access them.
- *
- * All objects <= SMALL_SIZE are automatically binned. In order to bin
- * objects between SMALL_SIZE and BIN_SIZE bytes the routine Mem_Bin
- * should be called. Note that Mem_Bin must be called before any objects
- * of the size to be binned have been allocated.
- */
-
- #ifdef MEM_TRACE
-
- typedef struct {
- int admin; /* Administrative word. */
- Address pc; /* PC of call to Mem_Alloc. */
- int origSize; /* Original size given to Mem_Alloc. */
- int padding; /* To make it double word aligned */
- } AdminInfo;
-
- #define GET_ADMIN(blockPtr) (((AdminInfo *) (blockPtr))->admin)
- #define SET_ADMIN(blockPtr, value) \
- ((AdminInfo *) (blockPtr))->admin = (int) (value)
-
- #define GET_PC(blockPtr) (((AdminInfo *) (blockPtr))->pc)
- #define SET_PC(blockPtr) ((AdminInfo *) (blockPtr))->pc = Mem_CallerPC()
-
- #define GET_ORIG_SIZE(blockPtr) (((AdminInfo *) (blockPtr))->origSize)
- #define SET_ORIG_SIZE(blockPtr,size) \
- ((AdminInfo *) (blockPtr))->origSize = size
-
- #else /* MEM_TRACE */
- #if defined(sequent)
-
- /*
- * Need 16-byte alignment for Sequent Symmetry due to various IO devices
- * (eg, SCED needs 8-byte alignment, DCC needs 16-byte). Would be nicer if
- * all upper level code doing IO insured the buffers were aligned (hence avoid
- * extra fragmentation here), but will live with this. Note that this isn't a
- * problem in Sequent Dynix, since all IO buffers are implicitly aligned.
- *
- * Note that can probably align the bin'd objects differently so that the
- * admin word lives just before a 16-byte boundary. This is more than I want
- * to modify right now ;-)
- *
- * This assumes Vm_RawAlloc() returns 16-byte aligned data, as well.
- */
-
- typedef struct {
- int admin; /* Administrative word. */
- int padding[3]; /* To make it 16-byte aligned */
- } AdminInfo;
-
- #define GET_ADMIN(blockPtr) (((AdminInfo *) (blockPtr))->admin)
- #define SET_ADMIN(blockPtr, value) \
- ((AdminInfo *) (blockPtr))->admin = (int) (value)
- #else /* sequent */
- typedef double AdminInfo;
-
- #define GET_ADMIN(blockPtr) (*(int *)(AdminInfo *) (blockPtr))
- #define SET_ADMIN(blockPtr, value) *((int *)(AdminInfo *) (blockPtr)) = (int) (value)
- #endif /* sequent */
- #endif /* MEM_TRACE */
-
- #define USE_BIT 1
- #define DUMMY_BITS 3
- #define ADMIN_BITS(admin) ((admin) & DUMMY_BITS)
- #define IS_IN_USE(admin) ((admin) & USE_BIT)
- #define IS_DUMMY(admin) (((admin) & DUMMY_BITS) == DUMMY_BITS)
- #define MARK_DUMMY(admin) ((admin) | DUMMY_BITS)
- #define MARK_IN_USE(admin) ((admin) | USE_BIT)
- #define MARK_FREE(admin) ((admin) & ~DUMMY_BITS)
- #define SIZE(admin) ((admin) & ~DUMMY_BITS)
-
- /*
- * The memory manager is a monitor. The following definition is
- * for its monitor lock.
- */
-
- static Sync_Lock memMonitorLock = Sync_LockInitStatic("memMonitorLock");
- #define LOCKPTR (&memMonitorLock)
-
- static int mem_NumAllocs = 0;
- static int mem_NumFrees = 0;
-
- #define MAX_TO_PRINT 256
- static struct {
- int size; /* Size of the block. */
- int num; /* Number of blocks allocated. */
- int free; /* Number of blocks freed. */
- int inUse; /* Number of blocks still in use. */
- int dummy; /* Number of blocks used as dummy blocks. */
- } topN[MAX_TO_PRINT + 1];
-
- static void Init _ARGS_((void));
-
- #ifdef MEM_TRACE
- INTERNAL static void PrintTrace _ARGS_((Boolean allocated,
- register Address infoPtr, Address curPC));
- INTERNAL static void DoTrace _ARGS_((Boolean allocated,
- register Address infoPtr, Address curPC, int size));
- #endif
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Binned-object allocator: it keeps a separate linked free list
- * for each size of object less than SMALL_SIZE bytes in size and
- * a free list for all objects less than BIN_SIZE that have been
- * specified as being binned by a call to Mem_Bin.
- * The blocks on each list are linked together via their
- * administrative words, terminated by a NIL pointer. Allocation
- * and freeing are done in a stack-like manner from the front of
- * the free lists.
- *
- * Definitions (these are all related: if you change one, you'll
- * have to change several):
- *
- * GRANULARITY: The difference in size between succesive buckets. This
- * is determined by data alignment restrictions and should be
- * a power of 2 to allow for shifting to map from size to bucket.
- * SIZE_SHIFT: The shift distance to convert between size and bucket.
- * SMALL_SIZE: largest blocksize (total including administrative word)
- * managed by default by the binned-object allocator. This
- * should be 1 less than a multiple of the granularity.
- * BIN_SIZE: largest possible that can be binned. Should be one less
- * than a multiple of the granularity.
- *
- * These can be computed from the above constants.
- *
- * SMALL_BUCKETS: default number of separate free lists.
- * BIN_BUCKETS: number of binned free lists.
- * BYTES_TO_BLOCKSIZE: how many bytes a block must contain (total
- * including admin. word) to satisfy a user request in bytes.
- * BLOCKSIZE_TO_INDEX: which free list to use for blocks of a given
- * size (total including administrative word).
- * INDEX_TO_BLOCKSIZE: the (maximum) size corresponding to a bucket.
- * This will include the size of the administrative word.
- *
- * These constants determine how many new blocks are allocated to
- * a bin at one time.
- *
- * MIN_BLOCKS_AT_ONCE: The amount allocate the first time, and the amount
- * to increment the number of blocks each successive time.
- * MIN_SHIFT: The log2 of MIN_BLOCKS_AT_ONCE.
- * MAX_BLOCKS_AT_ONCE: the most number of blocks to be allocated at once.
- *
- * ----------------------------------------------------------------------------
- */
-
- #if defined(sequent)
-
- /*
- * 16 byte alignment to for Sequent Symmetry. Making granularity also 16
- * bytes has nice cache effects (at least thru Model B; this isn't
- * functionally important, but it may affect performance).
- */
- #define GRANULARITY 16
- #define SIZE_SHIFT 4
-
- /*
- * This SMALL_SIZE determined by examination of memory tracing, 1/89.
- * The BIN_SIZE allows us to bin an FS_BLOCK_SIZE object.
- */
- #define SMALL_SIZE 271
- #define BIN_SIZE 4207
-
- #else /* sequent */
-
- /*
- * 8 byte granularity to handle SPUR and SPARC.
- */
- #define GRANULARITY 8
- #define SIZE_SHIFT 3
- /*
- * This SMALL_SIZE determined by examination of memory tracing, 1/89.
- * The BIN_SIZE allows us to bin an FS_BLOCK_SIZE object.
- */
- #define SMALL_SIZE 271
- #define BIN_SIZE 4199
-
- #endif /* sequent */
-
- #define SMALL_BUCKETS ((SMALL_SIZE + 1) / GRANULARITY)
- #define BIN_BUCKETS ((BIN_SIZE + 1) / GRANULARITY)
- #define MASK (GRANULARITY - 1)
- #define BYTES_TO_BLOCKSIZE(bytes) ((bytes+MASK+sizeof(AdminInfo)) & (~MASK))
- #define BLOCKSIZE_TO_INDEX(size) ((size) >> SIZE_SHIFT)
- #define INDEX_TO_BLOCKSIZE(index) ((index) << SIZE_SHIFT)
- /*
- * These are set up to allocate 4, 8, 12, and then 16 blocks at a time.
- */
- #define MIN_BLOCKS_AT_ONCE 4
- #define MIN_SHIFT 2
- #define MAX_BLOCKS_AT_ONCE 16
-
- /*
- * The following array holds pointers to the first free blocks of each size.
- * Bucket i holds blocks whose total length is INDEX_TO_BLOCKSIZE(i) bytes
- * (including the administrative info). Blocks from bucket i are used to
- * satisfy user requests ranging from INDEX_TO_BLOCKSIZE(i)-sizeof(AdminInfo)
- * bytes up to INDEX_TO_BLOCKSIZE(i)-1 bytes.
- * Buckets 0 and 1 are never used, since they correspond to blocks too small
- * to hold any information for the user.
- */
-
- static Address freeLists[BIN_BUCKETS];
-
- /*
- * Used to gather statistics about allocations:
- */
-
- static int numBlocks[BIN_BUCKETS]; /* Total number of existing blocks,
- * both allocated and free, for each
- * small size. */
- static int numAllocs[BIN_BUCKETS]; /* Total number of allocation requests
- * made for blocks of each small size.
- */
-
- /*
- * ----------------------------------------------------------------------------
- * Large-object allocator: used for blocks that aren't binned.
- * We expect there to be relatively few allocations
- * made by the large-object allocator. Since all the blocks it
- * allocates are relatively large, fragmentation should be less
- * than it would be if it allocated small blocks too. All of the
- * blocks, in use or not, are kept in a single linked list using
- * their administrative words. The address of the next block in
- * the list is found by adding the size of the current block to the
- * address of its administrative word. Things are complicated
- * slightly because the storage area managed by this allocator
- * may be discontiguous: there could be several large regions
- * that it manages, separated by gaps that are managed by some other
- * storage allocator (e.g. the binned-object allocator). In this
- * case, the gaps between regions are handled by making the gaps
- * look like allocated blocks, with an administrative word at the
- * end of one region that causes a skip to the beginning of the
- * next region. These blocks are called "dummy blocks", and have
- * special flag bits in their administrative words (see definitions
- * below). All the blocks within a region are linked together
- * in increasing order of address. However, the different regions
- * may appear in any order on the list. Two dummy blocks mark the
- * beginning and end of the list.
- * ----------------------------------------------------------------------------
- */
-
- static char *first, *last;
- static char _first[sizeof(AdminInfo) + 4], _last[sizeof(AdminInfo) + 4];
- /* Dummy blocks marking beginning and
- * end of free list. Last has a size of
- * zero. */
- static Address currentPtr; /* Next block to consider allocating.
- * Rotates circularly through free list. */
- int largestFree; /* This holds the size of the largest
- * free block that's been seen on the
- * free list. It's updated as the
- * list is traversed. */
-
- /*
- * Minimum size of new regions requested by large-object allocator
- * when storage runs out:
- */
-
- #define MIN_REGION_SIZE 2048
-
- static int numLargeBytes; /* Total amount of storage managed by
- * the large allocator. */
- static int numLargeAllocs; /* Total number of allocation requests
- * handled by the large allocator. */
- static int numLargeLoops; /* Number of iterations through the
- * inner block-checking loop of the
- * large allocator. */
-
- /*
- * ----------------------------------------------------------------------------
- * Miscellaneous declarations.
- * ----------------------------------------------------------------------------
- */
-
- /*
- * Flag to make sure Init gets called once.
- */
-
- static void Init _ARGS_((void));
- static int initialized = FALSE;
-
-
- #ifdef MEM_TRACE
-
- /*
- * When Mem_Alloc or Mem_Free is called, a trace record will be printed and/or
- * the number of blocks being used by the caller will be stored in the trace
- * array if the block size is in sizeTraceArray. The array is defined with
- * Mem_SetTraceSizes(). PrintTrace calls a default printing routine; it can
- * be changed with Mem_SetPrintProc().
- */
- #define MAX_NUM_TRACE_SIZES 20
- #define MAX_CALLERS_TO_TRACE 20
- static struct TraceElement {
- Mem_TraceInfo traceInfo;
- struct {
- Address callerPC;
- int numBlocks;
- } allocInfo[MAX_CALLERS_TO_TRACE];
- } sizeTraceArray[MAX_NUM_TRACE_SIZES];
-
- static int numSizesToTrace = 0;
-
- int mem_PrintLargeAllocPC = 0;
-
- #endif /* MEM_TRACE */
-
-
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Init --
- *
- * Initializes the dynamic storage allocator.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The storage allocation structures are initialized.
- *
- * ----------------------------------------------------------------------------
- */
-
- INTERNAL static void
- Init()
- {
- int i;
-
- /*
- * Clear out all of the bins.
- */
- for (i = BIN_BUCKETS - 1; i > SMALL_BUCKETS - 1; i--) {
- numBlocks[i] = -1;
- freeLists[i] = (Address) NIL;
- numAllocs[i] = 0;
- }
-
- /*
- * Clear out the small-object free lists.
- */
- for (; i >= 0; i--) {
- freeLists[i] = (Address) NIL;
- numBlocks[i] = 0;
- numAllocs[i] = 0;
- }
-
- /*
- * Initialize the large-object free list with two dummy blocks that
- * mark its beginning and end. These blocks are declared to be arrays
- * of characters. In order for malloc to work correctly they have to
- * be aligned on word boundaries.
- */
- #if 0
- if (((int) first) & 0x3) {
- panic("Mem: 'first' is not word-aligned");
- }
- if (((int) last) & 0x3) {
- panic("Mem: 'last' is not word-aligned");
- }
- #else
- first = (char *) (((long)_first + 3) & ~3);
- last = (char *) (((long)_last + 3) & ~3);
- #endif
-
- SET_ADMIN(first, MARK_DUMMY(last-first));
- SET_ADMIN(last, MARK_DUMMY(0));
- currentPtr = first;
- largestFree = 0;
- numLargeBytes = 0;
- numLargeAllocs = 0;
- numLargeLoops = 0;
-
- MemPrintInit();
- }
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_Bin --
- *
- * Make objects of the given size be binned.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The bin corresponding to blocks of the given size is initialized.
- *
- * ----------------------------------------------------------------------------
- */
- ENTRY void
- Mem_Bin(numBytes)
- int numBytes;
- {
- int index;
-
- LOCK_MONITOR;
-
- if (!initialized) {
- initialized = TRUE;
- Init();
- }
- if (mem_NumAllocs > 0) {
- MemPanic("Mem_Bin: Mem_Alloc already called.\n");
- }
- numBytes = BYTES_TO_BLOCKSIZE(numBytes);
- if (numBytes > BIN_SIZE) {
- UNLOCK_MONITOR;
- return;
- }
- index = BLOCKSIZE_TO_INDEX(numBytes);
- if (numBlocks[index] >= 0) {
- UNLOCK_MONITOR;
- return;
- }
- numBlocks[index] = 0;
-
- UNLOCK_MONITOR;
- }
-
-
- /*
- * ----------------------------------------------------------------------------
- *
- * malloc --
- *
- * This procedure allocates a chunk of memory.
- *
- * Results:
- * The return value is a pointer to an area of at least numBytes
- * bytes of free storage. If no memory is available, then this
- * procedure will never return: the MemChunkAlloc procedure
- * determines what will happen.
- *
- * Side effects:
- * The returned block is marked as allocated and will not be
- * allocated to anyone else until it is returned with a call
- * to free().
- *
- * ----------------------------------------------------------------------------
- */
-
-
- ENTRY Address
- malloc(numBytes)
- register unsigned int numBytes; /* How many bytes to allocate.
- * Must be > 0 */
- {
- register int size, admin;
- Address result;
- Address newRegion;
- int regionSize;
- #ifdef MEM_TRACE
- int origSize;
- #endif /* MEM_TRACE */
- register int index;
-
- LOCK_MONITOR;
-
-
- mem_NumAllocs++;
-
- if (!initialized) {
- initialized = TRUE;
- Init();
- }
-
- #ifdef MEM_TRACE
- origSize = numBytes;
- #endif /* MEM_TRACE */
-
- /*
- * Handle binned objects quickly, if possible.
- */
- numBytes = BYTES_TO_BLOCKSIZE(numBytes);
- index = BLOCKSIZE_TO_INDEX(numBytes);
- if (numBytes <= BIN_SIZE && numBlocks[index] != -1) {
- if (freeLists[index] == (Address) NIL) {
- register int blocksAtOnce;
-
- /*
- * There aren't currently any free objects of this size.
- * Call the client's function to obtain another region
- * from the system. While we're at it, get a whole bunch
- * of objects of this size once and put all but one back
- * on the free list.
- */
- if (numBlocks[index] < MAX_BLOCKS_AT_ONCE) {
- blocksAtOnce = ((numBlocks[index] >> MIN_SHIFT) + 1) *
- MIN_BLOCKS_AT_ONCE;
- } else {
- blocksAtOnce = MAX_BLOCKS_AT_ONCE;
- }
- regionSize = MemChunkAlloc(blocksAtOnce * numBytes, &newRegion);
-
- while (regionSize >= numBytes) {
- SET_ADMIN(newRegion, freeLists[index]);
- freeLists[index] = newRegion;
- numBlocks[index] += 1;
- newRegion += numBytes;
- regionSize -= numBytes;
- }
-
- /*
- * If we were given more bytes than we wanted, and there aren't
- * quite enough left to make a full block, put the leftovers
- * on the free list for the smallest size. This may still result
- * in GRANULARITY bytes leftover that we have to throw away.
- */
-
- while (regionSize >= (sizeof(AdminInfo) + GRANULARITY)) {
- SET_ADMIN(newRegion, freeLists[2]);
- freeLists[2] = newRegion;
- numBlocks[2] += 1;
- newRegion += (sizeof(AdminInfo) + GRANULARITY);
- regionSize -= (sizeof(AdminInfo) + GRANULARITY);
- }
- }
-
- result = freeLists[index];
- freeLists[index] = (Address) GET_ADMIN(result);
- SET_ADMIN(result, MARK_IN_USE(numBytes));
- numAllocs[index] += 1;
-
- #ifdef MEM_TRACE
- SET_PC(result);
- SET_ORIG_SIZE(result, origSize);
- DoTrace(TRUE, result, (Address)NULL, numBytes);
- #endif /* MEM_TRACE */
-
- UNLOCK_MONITOR;
- return(result+sizeof(AdminInfo));
- }
-
- /*
- * This is a large object. Step circularly through the blocks
- * in the list, looking for one that's large enough to hold
- * what's needed. Move currentPtr to the next block in the list to
- * make sure that we make forward progress each time Mem_Alloc is called.
- * If we don't then there is an instability in the memory allocator
- * with the following pattern:
- *
- * while (TRUE) {
- * addr = malloc(4096);
- * free(addr);
- * malloc(248);
- * }
- *
- * This pattern causes memory to get heavily fragmented.
- */
-
- numLargeAllocs += 1;
- admin = GET_ADMIN(currentPtr);
- currentPtr += SIZE(admin);
- while (TRUE) {
- Address nextPtr;
-
- numLargeLoops += 1;
- admin = GET_ADMIN(currentPtr);
- size = SIZE(admin);
- nextPtr = currentPtr+size;
- if (!IS_IN_USE(admin)) {
-
- /*
- * Several blocks in a row could have been freed since the last
- * time we were here. If so, merge them together.
- */
-
- while (!IS_IN_USE(GET_ADMIN(nextPtr))) {
- size += SIZE(GET_ADMIN(nextPtr));
- admin = MARK_FREE(size);
- SET_ADMIN(currentPtr, admin);
- nextPtr = currentPtr+size;
- }
- if (size >= numBytes) {
- break;
- }
- if (size > largestFree) {
- largestFree = size;
- }
- }
-
- /*
- * This block won't do the job. Go on to the next block.
- * If the next block is the end of the list, then go back
- * to the beginning and start again, unless no storage has
- * been freed since the last time we went back. In this
- * case, allocate a new region of storage.
- */
-
- if (nextPtr != last) {
- currentPtr = nextPtr;
- continue;
- }
-
- if (largestFree > numBytes) {
- largestFree = 0;
- currentPtr = first;
- continue;
- }
-
- if (numBytes < MIN_REGION_SIZE) {
- regionSize = MemChunkAlloc(MIN_REGION_SIZE, &newRegion);
- } else {
- regionSize = MemChunkAlloc(numBytes + sizeof(AdminInfo),
- &newRegion);
- }
- numLargeBytes += regionSize;
-
- /*
- * If the new region immediately follows the end of the previous
- * region, merge the two together (at this point currentPtr always
- * points to a dummy block whose administrative word points to "last").
- * If we can't merge, then make currentPtr link to the new area.
- */
-
- if (newRegion == (currentPtr+sizeof(AdminInfo))) {
- newRegion = currentPtr;
- regionSize += sizeof(AdminInfo);
- } else {
- SET_ADMIN(currentPtr, MARK_DUMMY(newRegion - currentPtr));
- }
-
- /*
- * Create a dummy block at the end of the new region, which links
- * to "last".
- */
-
- SET_ADMIN(newRegion, MARK_FREE(regionSize - sizeof(AdminInfo)));
- newRegion += regionSize - sizeof(AdminInfo);
- SET_ADMIN(newRegion, MARK_DUMMY(last - newRegion));
-
- /*
- * Continue scanning the list (try currentPtr again in case it
- * merged with the new region).
- */
- }
-
- /*
- * At this point we've got a block that's large enough. If it's
- * larger than needed for the object, put the rest back on the
- * free list (note: even if the remnant is smaller than the smallest
- * large object, which means it'll be used by itself, we put it back
- * on the list so it can merge with either this block or the next,
- * whichever gets freed first).
- */
-
- if (size == numBytes) {
- SET_ADMIN(currentPtr, MARK_IN_USE(admin));
- } else {
- SET_ADMIN(currentPtr+numBytes, MARK_FREE(size-numBytes));
- SET_ADMIN(currentPtr, MARK_IN_USE(numBytes));
- }
-
- #ifdef MEM_TRACE
- SET_PC(currentPtr);
- SET_ORIG_SIZE(currentPtr, origSize);
- DoTrace(TRUE, currentPtr, (Address)NULL, numBytes);
- #endif /* MEM_TRACE */
-
- result = currentPtr;
- UNLOCK_MONITOR;
- return(result+sizeof(AdminInfo));
- }
-
-
- /*
- * ----------------------------------------------------------------------------
- *
- * _free --
- *
- * Return a previously-allocated block of storage to the free pool.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The storage pointed to by blockPtr is marked as free and returned
- * to the free pool. Nothing in the bytes pointed to by blockPtr is
- * modified at this time: no change will occur until at least the
- * next call to Mem_Alloc. This means that callers may use the contents
- * of a block for a short time after free-ing it (e.g. to read a
- * "next" pointer).
- *
- * ----------------------------------------------------------------------------
- */
-
- ENTRY int
- free(blockPtr)
- Address blockPtr;
- {
- _free(blockPtr);
- return 0;
- }
-
- ENTRY void
- _free(blockPtr)
- Address blockPtr; /* Pointer to storage to be freed. Must
- * have been the return value from Mem_Alloc
- * at some previous time. */
- {
- register int admin;
- register int index;
- register int size;
-
- LOCK_MONITOR;
-
- mem_NumFrees++;
-
- if (!initialized) {
- MemPanic("Mem_Free: allocator not initialized!\n");
- return; /* should never get here */
- }
-
- /*
- * Make sure that this block bears some resemblance to a
- * well-formed storage block.
- */
-
- blockPtr -= sizeof(AdminInfo);
- admin = GET_ADMIN(blockPtr);
- if (ADMIN_BITS(admin) != USE_BIT) {
- if (!IS_IN_USE(admin)) {
- if (!memAllowFreeingFree) {
- MemPanic("Mem_Free: storage block already free\n");
- }
- } else {
- MemPanic("Mem_Free: storage block is corrupted\n");
- }
- UNLOCK_MONITOR;
- return; /* (should never get here) */
- }
-
- /* This procedure is easier for large blocks than for small ones.
- * If large, just clear the use bit. Since this block could merge
- * with its neighbors, we don't really know anymore what's the
- * largest size on the free list, so set largestFree to a very
- * large number. This guarantees that the allocator will check
- * the whole list again before requesting additional memory.
- */
-
- size = SIZE(admin);
- index = BLOCKSIZE_TO_INDEX(size);
- if (size > BIN_SIZE || numBlocks[index] == -1) {
- SET_ADMIN(blockPtr, MARK_FREE(admin));
- largestFree = numLargeBytes;
- } else {
- /*
- * For small blocks, add the block back onto its free list.
- */
-
- SET_ADMIN(blockPtr, freeLists[index]);
- freeLists[index] = blockPtr;
- }
-
- #ifdef MEM_TRACE
- DoTrace(FALSE, blockPtr, Mem_CallerPC(), size);
- #endif /* MEM_TRACE */
-
- UNLOCK_MONITOR;
- return;
- }
-
- /*
- * Global variables that can be set to control thresholds for printing
- * statistics.
- */
-
- static int mem_SmallMinNum = 1; /* There must be at least this many
- * binned objects of a size before info
- * about its size gets printed. */
- static int mem_LargeMinNum = 1; /* There must be at least this many
- * non-binned objects of a size before
- * info about the size gets printed. */
- static int mem_LargeMaxSize = 10000; /* Info is printed for non-binned
- * objects larger than this regardless
- * of how many of them there are. */
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_Size --
- *
- * Return the size of a previously-allocated storage block.
- *
- * Results:
- * The return value is the size of *blockPtr, in bytes. This is
- * the total usable size of the block. It may be slightly greater
- * than the size actually requested in the Mem_Alloc, since this
- * module rounds sizes up to convenient boundaries.
- *
- * Side effects:
- * None.
- *
- * ----------------------------------------------------------------------------
- */
-
- ENTRY int
- Mem_Size(blockPtr)
- Address blockPtr; /* Pointer to storage to be freed. Must have been the
- * return value from Mem_Alloc at some previous time. */
- {
- int admin;
-
- LOCK_MONITOR;
-
- if (!initialized) {
- MemPanic("Mem_Size: allocator not initialized!\n");
- UNLOCK_MONITOR;
- return(0); /* should never get here */
- }
-
- /*
- * Make sure that this block bears some resemblance to a
- * well-formed storage block.
- */
-
- blockPtr -= sizeof(AdminInfo);
- admin = GET_ADMIN(blockPtr);
- if (ADMIN_BITS(admin) != USE_BIT) {
- if (!IS_IN_USE(admin)) {
- MemPanic("Mem_Size: storage block is free\n");
- } else {
- MemPanic("Mem_Size: storage block is corrupted\n");
- }
- UNLOCK_MONITOR;
- return(0); /* (should never get here) */
- }
-
- UNLOCK_MONITOR;
- return(SIZE(admin) - sizeof(AdminInfo));
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Mem_PrintStats --
- *
- * Print out memory statistics with Mem_PrintStatsSubr using the
- * default printing routine and default sizes.
- *
- * See Mem_PrintStatsSubrInt for details.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- ENTRY void
- Mem_PrintStats()
- {
- LOCK_MONITOR;
-
- Mem_PrintStatsSubrInt(memPrintProc, memPrintData, mem_SmallMinNum,
- mem_LargeMinNum, mem_LargeMaxSize);
-
- UNLOCK_MONITOR;
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Mem_PrintStatsInt --
- *
- * Print out memory statistics with Mem_PrintStatsSubr using the
- * default printing routine and default sizes. Same as Mem_PrintStats
- * except that this routine does not grab the monitor lock. This is
- * intended to be used to print out memory stats when the operating
- * system runs out of memory after being called by Mem_Alloc and hence
- * the monitor is already down.
- *
- * See Mem_PrintStatsSubrInt for details.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- void
- Mem_PrintStatsInt()
- {
- Mem_PrintStatsSubrInt(memPrintProc, memPrintData, mem_SmallMinNum,
- mem_LargeMinNum, mem_LargeMaxSize);
- }
-
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_PrintStatsSubr --
- *
- * This procedure prints out statistics about memory allocation
- * so far. Grabs monitor lock and calls the unmonitored routine
- * Mem_PrintStatsSubrInt.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- * ----------------------------------------------------------------------------
- */
- ENTRY void
- Mem_PrintStatsSubr(printProc, clientData, smallMinNum, largeMinNum,
- largeMaxSize)
- void (*printProc)(); /* Procedure to actually print stuff. */
- ClientData clientData; /* Argument to pass to printProc. */
- int smallMinNum; /* Minimum number of elements in a bucket
- * before will print out stats. */
- int largeMinNum; /* Minimum number of large objects of a given
- * size before will print out stats about
- * the size. */
- int largeMaxSize; /* Statistics will be printed about all blocks
- * over size regardless of how many there are*/
- {
- LOCK_MONITOR;
-
- Mem_PrintStatsSubrInt(printProc, clientData, smallMinNum, largeMinNum,
- largeMaxSize);
-
- UNLOCK_MONITOR;
- }
-
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_PrintConfig --
- *
- * Prints out the exact configuration of the dynamic memory allocator
- * using the default printing routine.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- * ----------------------------------------------------------------------------
- */
-
- void
- Mem_PrintConfig()
- {
- Mem_PrintConfigSubr(memPrintProc, memPrintData);
- }
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_PrintConfigSubr --
- *
- * Uses a client-supplied procedure to print out the exact
- * configuration of the dynamic memory allocator.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Stuff gets printed (?) by passing it to printProc. See the
- * documentation in Mem_PrintStatsSubr for the calling sequence to
- * printProc.
- *
- * ----------------------------------------------------------------------------
- */
-
- ENTRY void
- Mem_PrintConfigSubr(printProc, clientData)
- void (*printProc)(); /* Procedure to actually print stuff. */
- ClientData clientData; /* Argument to pass to printProc. */
- {
- register Address ptr;
- #ifdef VERBOSE
- int i, j;
- #endif /* VERBOSE */
-
- LOCK_MONITOR;
-
- if (!initialized) {
- (*printProc)(clientData, "Allocator not initialized yet.\n");
- return;
- }
-
- #ifdef VERBOSE
- (*printProc)(clientData, "Small object allocator:\n");
- for (i = 2; i < BIN_BUCKETS; i++) {
- if (freeLists[i] == (Address) NIL) {
- continue;
- }
- (*printProc)(clientData, " %d bytes:", i*GRANULARITY);
- j = 5;
- for (ptr = freeLists[i]; ptr != (Address) NIL;
- ptr = (Address) GET_ADMIN(ptr)) {
- if (j == 5) {
- (*printProc)(clientData, "\n ");
- j = 0;
- } else {
- j += 1;
- }
- (*printProc)(clientData, "%12#x", ptr);
- }
- (*printProc)(clientData, "\n");
- }
- #endif /* VERBOSE */
-
- (*printProc)(clientData, "Large object allocator:\n");
-
- #ifdef MEM_TRACE
- (*printProc)(clientData, " Location Orig. Size State\n");
- #else
- (*printProc)(clientData, " Location Size State\n");
- #endif /* MEM_TRACE */
-
- for (ptr = first + SIZE(GET_ADMIN(first)); ptr != last;
- ptr += SIZE(GET_ADMIN(ptr))) {
-
- #ifdef MEM_TRACE
- (*printProc)(clientData, "%12#x %10d", ptr, GET_ORIG_SIZE(ptr));
- if (IS_DUMMY(GET_ADMIN(ptr))) {
- (*printProc)(clientData, " Dummy\n");
- } else if (IS_IN_USE(GET_ADMIN(ptr))) {
- (*printProc)(clientData, " In use (PC=0x%x)\n", GET_PC(ptr));
- #else
- (*printProc)(clientData, "%12#x %10d", ptr, SIZE(GET_ADMIN(ptr)));
- if (IS_DUMMY(GET_ADMIN(ptr))) {
- (*printProc)(clientData, " Dummy\n");
- } else if (IS_IN_USE(GET_ADMIN(ptr))) {
- (*printProc)(clientData, " In use\n");
- #endif /* MEM_TRACE */
- } else {
- (*printProc)(clientData, " Free\n");
- }
- }
- UNLOCK_MONITOR;
- }
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_PrintInUse --
- *
- * Uses a default procedure to print out the blocks in allocator
- * that are still in use. The original size and the PC of the
- * call to Mem_Alloc are printed.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Stuff gets printed (?) by passing it to memPrintProc. See the
- * documentation in Mem_PrintStatsSubr for the calling sequence to
- * memPrintProc.
- *
- * ----------------------------------------------------------------------------
- */
-
- ENTRY void
- Mem_PrintInUse()
- {
- #ifdef MEM_TRACE
- register Address ptr;
-
- LOCK_MONITOR;
-
- if (!initialized) {
- (*memPrintProc)(memPrintData, "Allocator not initialized yet.\n");
- return;
- }
-
- (*memPrintProc)(memPrintData, "Large objects still in use:\n");
- (*memPrintProc)(memPrintData, " Location Orig. Size Alloc.PC\n");
-
- for (ptr = first + SIZE(GET_ADMIN(first)); ptr != last;
- ptr += SIZE(GET_ADMIN(ptr))) {
-
- if (!IS_DUMMY(GET_ADMIN(ptr)) && IS_IN_USE(GET_ADMIN(ptr))) {
- (*memPrintProc)(memPrintData,"%12#x %10d %12#x\n",
- ptr, GET_ORIG_SIZE(ptr), GET_PC(ptr));
- }
- }
- UNLOCK_MONITOR;
- #endif /* MEM_TRACE */
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Mem_SetTraceSizes --
- *
- * Defines a list of sizes to trace and causes tracing to start.
- * If the numSizes is zero or the array ptr is NULL, tracing is
- * turned off.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Traces of Mem_Alloc and Mem_Free start or end.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- ENTRY void
- Mem_SetTraceSizes(numSizes, arrayPtr)
- int numSizes; /* # of elements in arrayPtr */
- Mem_TraceInfo *arrayPtr; /* Array of block sizes to trace. */
- {
- #ifdef MEM_TRACE
- int i;
-
- LOCK_MONITOR;
-
- if (numSizes <= 0 || arrayPtr == (Mem_TraceInfo *)NIL ||
- arrayPtr == (Mem_TraceInfo *)NULL) {
- if (numSizes == -1) {
- numSizesToTrace = -1;
- } else {
- numSizesToTrace = 0;
- }
- UNLOCK_MONITOR;
- return;
- }
-
- if (numSizes > MAX_NUM_TRACE_SIZES) {
- numSizes = MAX_NUM_TRACE_SIZES;
- }
- numSizesToTrace = numSizes;
- for (i = 0; i < numSizes; i++) {
- sizeTraceArray[i].traceInfo = arrayPtr[i];
- sizeTraceArray[i].traceInfo.flags |= MEM_TRACE_NOT_INIT;
- }
- UNLOCK_MONITOR;
-
- #endif /* MEM_TRACE */
- }
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Mem_SetPrintProc --
- *
- * Changes the default printing routines
- *
- * Results:
- * None.
- *
- * Side effects:
- * The default printing routine is changed.
- *
- *----------------------------------------------------------------------
- */
-
- ENTRY void
- Mem_SetPrintProc(proc, data)
- void (*proc)(); /* Address of new print routine. */
- ClientData data; /* Data to be passed to proc. */
- {
- LOCK_MONITOR;
- if (proc != (void(*)())NIL &&
- proc != (void(*)()) NULL) {
- memPrintProc = proc;
- memPrintData = data;
- }
- UNLOCK_MONITOR;
- }
-
- /*
- * ----------------------------------------------------------------------------
- *
- * Mem_PrintStatsSubrInt --
- *
- * This procedure prints out statistics about memory allocation
- * so far.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The procedure printProc is called several times to print out
- * information. Its calling sequence is:
- *
- * void
- * printProc(clientData, format, arg1, arg2, arg3, arg4, arg5)
- * ClientData clientData;
- * char *format;
- *
- * This is identical to the calling sequence for the standard I/o
- * routine Io_Print. The first argument is the clientData argument
- * passed to this procedure, which need not necessarily be a
- * (Io_Stream *). There will never be more than 5 arguments.
- * A typical calling sequence is:
- * "Mem_PrintStatsSubr(Io_PrintStream, (ClientData) io_StdOut,
- * 50, 100, 1000)".
- *
- * ----------------------------------------------------------------------------
- */
- INTERNAL void
- Mem_PrintStatsSubrInt(printProc, clientData, smallMinNum, largeMinNum,
- largeMaxSize)
- void (*printProc)(); /* Procedure to actually print stuff. */
- ClientData clientData; /* Argument to pass to printProc. */
- int smallMinNum; /* Minimum number of elements in a bucket
- * before will print out stats. */
- int largeMinNum; /* Minimum number of large objects of a given
- * size before will print out stats about
- * the size. */
- int largeMaxSize; /* Statistics will be printed about all blocks
- * over size regardless of how many there are*/
- {
- register Address ptr;
- register int i;
- int totalBlocks, totalAllocs, totalFree;
- int allocBytes = 0;
- int freeBytes = 0;
- Boolean firstTime = TRUE;
- Boolean warnedAboutOverflow;
-
- if (!initialized) {
- (*printProc)(clientData, "Allocator not initialized yet.\n");
- return;
- }
-
- (*printProc)(clientData, "\nTotal allocs = %d, frees = %d\n\n",
- mem_NumAllocs, mem_NumFrees);
-
- (*printProc)(clientData, "Small object allocator:\n");
- totalBlocks = totalAllocs = totalFree = 0;
- for (i = 2; i < BIN_BUCKETS; i++) {
- int numFree = 0;
-
- if (numBlocks[i] <= 0) {
- continue;
- }
- allocBytes += numBlocks[i] * INDEX_TO_BLOCKSIZE(i);
- for (ptr = freeLists[i];
- ptr != (Address) NIL;
- ptr = (Address) GET_ADMIN(ptr)) {
-
- freeBytes += INDEX_TO_BLOCKSIZE(i);
- numFree += 1;
- }
- if (numBlocks[i] >= smallMinNum) {
- if (firstTime) {
- (*printProc)(clientData,
- " Size Total Allocs In Use\n");
- firstTime = FALSE;
- }
- (*printProc)(clientData, "%8d%10d%10d%10d\n",
- INDEX_TO_BLOCKSIZE(i), numBlocks[i],
- numAllocs[i], numBlocks[i] - numFree);
- }
- totalBlocks += numBlocks[i];
- totalAllocs += numAllocs[i];
- totalFree += numFree;
- }
- (*printProc)(clientData, " Total%10d%10d%10d\n", totalBlocks,
- totalAllocs, totalBlocks - totalFree);
- (*printProc)(clientData, "Bytes allocated = %d, freed = %d\n\n",
- allocBytes, freeBytes);
-
- /*
- * Initialize the largest N-sizes buffer.
- */
- for (i = 0; i < MAX_TO_PRINT + 1; i++) {
- topN[i].size = -1;
- topN[i].free = 0;
- topN[i].dummy = 0;
- }
-
- warnedAboutOverflow = FALSE;
- for (ptr = first + SIZE(GET_ADMIN(first));
- ptr != last;
- ptr += SIZE(GET_ADMIN(ptr))) {
-
- int admin;
- int size;
- Boolean found;
-
- admin = GET_ADMIN(ptr);
- if (!IS_DUMMY(admin) && !IS_IN_USE(admin)) {
- freeBytes += SIZE(admin);
- }
-
- size = SIZE(admin);
- #ifdef MEM_TRACE
- if (size - GET_ORIG_SIZE(ptr) < 4 &&
- size - GET_ORIG_SIZE(ptr) >= 0) {
- size = GET_ORIG_SIZE(ptr);
- }
- #endif /* MEM_TRACE */
-
- found = FALSE;
- for (i = 0; i < MAX_TO_PRINT; i++) {
- if (size == topN[i].size) {
- found = TRUE;
- topN[i].num++;
- if (IS_DUMMY(admin)) {
- topN[i].dummy++;
- } else if (IS_IN_USE(admin)) {
- topN[i].inUse++;
- } else {
- topN[i].free++;
- }
- break;
- } else if (topN[i].size == -1) {
- found = TRUE;
- topN[i].size = size;
- topN[i].num = 1;
- if (IS_DUMMY(admin)) {
- topN[i].dummy = 1;
- } else if (IS_IN_USE(admin)) {
- topN[i].inUse = 1;
- } else {
- topN[i].free = 1;
- }
- break;
- }
- }
-
- if (!found && !warnedAboutOverflow) {
- (*printProc)(clientData, "Largest-Size buffer overflow: %d\n",size);
- warnedAboutOverflow = TRUE;
- }
- }
- (*printProc)(clientData, "Large object allocator:\n");
- (*printProc)(clientData, " Total bytes managed: %d\n", numLargeBytes);
- (*printProc)(clientData, " Bytes in use: %d\n",
- numLargeBytes - freeBytes);
- firstTime = TRUE;
- for (i = 0; topN[i].size != -1; i++) {
- if ((topN[i].num >= largeMinNum || topN[i].size >= largeMaxSize) &&
- (topN[i].num != topN[i].dummy)) {
- if (firstTime) {
- (*printProc)(clientData, "%10s%10s%10s%10s\n",
- #ifdef MEM_TRACE
- "Orig. Size", "Num", "Free", "In Use");
- #else
- "Size", "Num", "Free", "In Use");
- #endif /* MEM_TRACE */
- firstTime = FALSE;
- }
- (*printProc)(clientData, "%10d%10d%10d%10d\n",
- topN[i].size, topN[i].num, topN[i].free, topN[i].inUse);
- }
- }
- (*printProc)(clientData, "\n");
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * DoTrace --
- *
- * Print and/or store a trace record.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- #ifdef MEM_TRACE
- INTERNAL static void
- DoTrace(allocated, infoPtr, curPC, size)
- Boolean allocated; /* If TRUE, we are called by Mem_Alloc,
- * otherwise by Mem_Free. */
- register Address infoPtr; /* Address of admin. info. */
- Address curPC; /* If called by Mem_Free, PC of
- * call to Mem_Free, NULL otherwise. */
- int size; /* Size actually allocated. */
- {
- int i, j;
- int origSize;
- Address callerPC;
- register struct TraceElement *trPtr;
-
- if (numSizesToTrace == -1) {
- PrintTrace(allocated, infoPtr, curPC);
- return;
- }
-
- callerPC = GET_PC(infoPtr);
-
- origSize = GET_ORIG_SIZE(infoPtr);
-
- for (i = 0, trPtr = sizeTraceArray; i < numSizesToTrace; i++, trPtr++) {
- if (trPtr->traceInfo.flags & MEM_DONT_USE_ORIG_SIZE) {
- if (trPtr->traceInfo.size != size) {
- continue;
- }
- } else if (trPtr->traceInfo.size != origSize) {
- continue;
- }
- if (trPtr->traceInfo.flags & MEM_PRINT_TRACE) {
- PrintTrace(allocated, infoPtr, curPC);
- }
- if (trPtr->traceInfo.flags & MEM_STORE_TRACE) {
- register int slot;
- if (trPtr->traceInfo.flags & MEM_TRACE_NOT_INIT) {
- for (j = 0; j < MAX_CALLERS_TO_TRACE; j++) {
- trPtr->allocInfo[j].numBlocks = 0;
- trPtr->allocInfo[j].callerPC = 0;
- }
- trPtr->traceInfo.flags &= ~MEM_TRACE_NOT_INIT;
- }
- slot = -1;
- for (j = 0; j < MAX_CALLERS_TO_TRACE; j++) {
- if (trPtr->allocInfo[j].callerPC == callerPC) {
- if (allocated) {
- trPtr->allocInfo[j].numBlocks++;
- } else {
- trPtr->allocInfo[j].numBlocks--;
- }
- return;
- }
- if ((trPtr->allocInfo[j].numBlocks == 0) && (slot < 0)) {
- slot = j;
- }
- }
- if ((slot >= 0) && allocated) {
- trPtr->allocInfo[slot].callerPC = callerPC;
- trPtr->allocInfo[slot].numBlocks = 1;
- }
- }
- return;
- }
- if (size > BIN_SIZE && mem_PrintLargeAllocPC) {
- printf("malloc(%d[%d]) from PC 0x%x\n", origSize, size, callerPC);
- }
- }
- #endif /* MEM_TRACE */
-
-
-
- /*
- *----------------------------------------------------------------------
- *
- * Mem_DumpTrace --
- *
- * Dump the allocation trace records for the given size block. If
- * the size is specified as -1 then all trace records for all size
- * blocks are dumped.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- /*ARGSUSED*/
- void
- Mem_DumpTrace(blockSize)
- int blockSize;
- {
- #ifdef MEM_TRACE
- register struct TraceElement *trPtr;
- int i, j;
-
- for (i = 0, trPtr = sizeTraceArray; i < numSizesToTrace; i++, trPtr++) {
- if (trPtr->traceInfo.size != blockSize && blockSize != -1) {
- continue;
- }
- if (!(trPtr->traceInfo.flags & MEM_STORE_TRACE) ||
- (trPtr->traceInfo.flags & MEM_TRACE_NOT_INIT)) {
- continue;
- }
-
- if (trPtr->traceInfo.flags & MEM_DONT_USE_ORIG_SIZE) {
- (*memPrintProc)(memPrintData, "Trace for block size = %d:\n",
- trPtr->traceInfo.size);
- } else {
- (*memPrintProc)(memPrintData, "Trace for size = %d (%d):\n",
- trPtr->traceInfo.size,
- BYTES_TO_BLOCKSIZE(trPtr->traceInfo.size));
- }
- (*memPrintProc)(memPrintData, "Caller-PC Num-blocks \n");
- for (j = 0; j < MAX_CALLERS_TO_TRACE; j++) {
- if (trPtr->allocInfo[j].numBlocks == 0) {
- break;
- }
- (*memPrintProc)(memPrintData, "%8x %6d\n",
- trPtr->allocInfo[j].callerPC,
- trPtr->allocInfo[j].numBlocks);
- }
- if (blockSize != -1) {
- break;
- }
- }
- #endif
- }
-
-
-
- /*
- *----------------------------------------------------------------------
- *
- * PrintTrace --
- *
- * Print out the given trace information about a memory trace record.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- #ifdef MEM_TRACE
- INTERNAL static void
- PrintTrace(allocated, infoPtr, curPC)
- Boolean allocated; /* If TRUE, we are called by Mem_Alloc,
- * otherwise by Mem_Free. */
- register Address infoPtr; /* Address of admin. info. */
- Address curPC; /* If called by Mem_Free, PC of
- * call to Mem_Free, NULL otherwise. */
- {
- if (allocated) {
- (*memPrintProc)(memPrintData,
- "Mem_Alloc: PC=0x%x addr=0x%x size=%d\n",
- GET_PC(infoPtr), infoPtr+sizeof(AdminInfo),
- GET_ORIG_SIZE(infoPtr));
- } else {
- (*memPrintProc)(memPrintData,
- "Mem_Free: PC=0x%x addr=0x%x size=%d *\n",
- curPC, infoPtr+sizeof(AdminInfo), GET_ORIG_SIZE(infoPtr));
- }
- }
- #endif /* MEM_TRACE */
-
-